import 'package:efood_multivendor/data/api/api_checker.dart';
import 'package:efood_multivendor/data/model/body/signin_body.dart';
import 'package:efood_multivendor/data/model/body/signup_body.dart';
import 'package:efood_multivendor/data/model/response/response_model.dart';
import 'package:efood_multivendor/data/repository/auth_repo.dart';
import 'package:efood_multivendor/helper/enums.dart';
import 'package:efood_multivendor/helper/fcm_helper.dart';
import 'package:efood_multivendor/helper/route_helper.dart';
import 'package:efood_multivendor/util/app_constants.dart';

import 'package:efood_multivendor/view/base/custom_snackbar.dart';
import 'package:efood_multivendor/view/screens/auth/sign_up_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

import 'package:get/get.dart';

class AuthController extends GetxController implements GetxService {
  final AuthRepo authRepo;

  AuthController({required this.authRepo}) {
    _notification = authRepo.isNotificationActive();
  }

  bool _isLoading = false;
  bool _notification = true;
  bool _acceptTerms = true;
  bool _isWaitingForOTP = false;
  String _verificationId = '';
  late SignUpBody _signUpBody;
  String _otpChannel = '';

  bool get isWaitingForOTP => _isWaitingForOTP;

  bool get isLoading => _isLoading;

  bool get notification => _notification;

  bool get acceptTerms => _acceptTerms;

  String get verificationId => _verificationId;

  SignUpBody get signUpBody => _signUpBody;

  String get otpChannel => _otpChannel;

  void onInit() {
    super.onInit();
  }

  Future<void> updateToken() async {
    if (!isLoggedIn()) return;
    FirebaseHelper.instance.getToken().then((token) {
      updateDevicesToken(token);
    });
  }

  Future<void> updateDevicesToken(String? newToken) async {
    if (!isLoggedIn()) return;
    if (newToken == null) {
      newToken = await FirebaseHelper.instance.getToken();
    }
    try {
      await FirebaseHelper.instance.requestNotificationPermission();
      if (!GetPlatform.isWeb) {
        await FirebaseMessaging.instance.subscribeToTopic(AppConstants.TOPIC);
      }
    } finally {
      await authRepo.updateToken(newToken);
    }
  }

  Future<ResponseModel> registerUser(name, refCode) async {
    _isLoading = true;
    update();

    Response response = await authRepo.registerUser(name, refCode);
    ResponseModel responseModel;
    if (response.statusCode == 200) {
      responseModel = ResponseModel(true, response.body["message"]);
    } else {
      responseModel = ResponseModel(false, response.statusText);
    }
    _isLoading = false;
    update();
    return responseModel;
  }

  Future<ResponseModel> verifyPhone(String phone, String token) async {
    _isLoading = true;
    update();
    Response response = await authRepo.verifyPhone(phone, _verificationCode);
    ResponseModel responseModel;
    if (response.statusCode == 200) {
      authRepo.saveUserToken(token);
      await updateToken();
      responseModel = ResponseModel(true, response.body["message"]);
    } else {
      responseModel = ResponseModel(false, response.statusText);
    }
    _isLoading = false;
    update();
    return responseModel;
  }

  String _verificationCode = '';

  String get verificationCode => _verificationCode;

  void updateVerificationCode(String query) {
    _verificationCode = query;
    update();
  }

  void sendOTP(String _phone, String countryDialCode) async {
    _isLoading = true;
    update();

    String _numberWithCountryCode = countryDialCode + _phone;
    bool _isValid = GetPlatform.isWeb ? true : false;
    if (!GetPlatform.isWeb) {
      if (_numberWithCountryCode.length == 13) {
        _isValid = true;
      } else {
        _isLoading = false;
        update();
      }
    }
    if (_phone.isEmpty) {
      showCustomSnackBar('enter_phone_number'.tr);
    } else if (!_isValid) {
      showCustomSnackBar('invalid_phone_number'.tr);
    } else {
      verifyNumber(_numberWithCountryCode);
    }
  }

  Future<void> verifyNumber(String phonenumber) async {
    try {
      await FirebaseAuth.instance
          .verifyPhoneNumber(
        phoneNumber: phonenumber,
        verificationCompleted: (PhoneAuthCredential credential) async {
          try {
            // UserCredential userCredential =
            await FirebaseAuth.instance.signInWithCredential(credential);
            String? token =
                await FirebaseAuth.instance.currentUser?.getIdToken();
            var loginResponse = await loginUser(token!);

            if (!loginResponse.isSuccess) {
              showCustomSnackBar(loginResponse.message);
              return;
            }

            if (loginResponse.message == 'newuser') {
              Get.to(SignUpScreen(phone: phonenumber));
            } else {
              Get.toNamed(RouteHelper.getInitialRoute("SplashScreen"));
            }

            _isLoading = false;
            waitForOTP(false);
          } catch (e, stackTrace) {
            FirebaseCrashlytics.instance.recordError(e, stackTrace);
            print("Login error sign in with credential: $e");
            print(stackTrace.toString());
            _isLoading = false;
          }
        },
        verificationFailed: (FirebaseAuthException e) {
          FirebaseCrashlytics.instance.recordError(e, null);
          print('Verification failed: ${e.toString()}');
          print('Code: ${e.code}');
          print('Message: ${e.message}');
          _isLoading = false;
        },
        codeSent: (String verificationId, int? resendToken) {
          _isLoading = false;
          setVerificationId(verificationId);
          waitForOTP(true);
        },
        codeAutoRetrievalTimeout: (String verificationId) {},
      )
          .onError((error, stackTrace) {
        FirebaseCrashlytics.instance.recordError(error, stackTrace);
        print("Login error on verify number: ${error.toString()}");
        print(stackTrace.toString());
        _isLoading = false;
      });
    } catch (e, stackTrace) {
      FirebaseCrashlytics.instance.recordError(e, stackTrace);
      print("Login error in verifyNumber function: ${e.toString()}");
      print(stackTrace.toString());
      _isLoading = false;
    }
  }

  bool _isActiveRememberMe = false;

  bool get isActiveRememberMe => _isActiveRememberMe;

  void toggleTerms() {
    _acceptTerms = !_acceptTerms;
    update();
  }

  void toggleRememberMe() {
    _isActiveRememberMe = !_isActiveRememberMe;
    update();
  }

  bool isLoggedIn() {
    return authRepo.isLoggedIn();
  }

  bool clearSharedData() {
    return authRepo.clearSharedData();
  }

  void saveUserNumberAndPassword(
      String number, String password, String countryCode) {
    authRepo.saveUserNumberAndPassword(number, password, countryCode);
  }

  String getUserNumber() {
    return authRepo.getUserNumber();
  }

  String getUserCountryCode() {
    return authRepo.getUserCountryCode();
  }

  String getUserPassword() {
    return authRepo.getUserPassword();
  }

  Future<bool> clearUserNumberAndPassword() async {
    return authRepo.clearUserNumberAndPassword();
  }

  String getUserToken() {
    return authRepo.getUserToken();
  }

  void waitForOTP(bool val) {
    _isWaitingForOTP = val;
    update();
  }

  Future<ResponseModel> loginUser(String? token) async {
    ResponseModel responseModel;
    Response res = await authRepo.sendFirebaseToken(token);
    if (res.statusCode == 200) {
      authRepo.saveUserToken(res.body['token']);
      await updateToken();
      if (res.body['userName'] == null) {
        responseModel = ResponseModel(true, 'newuser');
      } else {
        responseModel = ResponseModel(true, 'olduser');
      }
    } else {
      responseModel = ResponseModel(false, res.statusText);
    }

    return responseModel;
  }

  bool setNotificationActive(bool isActive) {
    _notification = isActive;
    if (isActive) updateToken();
    authRepo.setNotificationActive(isActive);
    update();
    return _notification;
  }

  void verifyOTP(String pin, String phone) async {
    _isLoading = true;
    update();
    try {
      PhoneAuthCredential credential = PhoneAuthProvider.credential(
          verificationId: verificationId, smsCode: pin);
      UserCredential userCredential =
          await FirebaseAuth.instance.signInWithCredential(credential);
      String? token = await userCredential.user?.getIdToken();
      var value = await loginUser(token);

      _isLoading = false;
      if (!value.isSuccess) {
        showCustomSnackBar(value.message);
        return;
      }
      _isWaitingForOTP = false;
      if (value.message == 'newuser') {
        Get.to(SignUpScreen(phone: phone));
      } else {
        Get.toNamed(RouteHelper.getInitialRoute("SplashScreen"));
      }
      update();
    } catch (error, stackTrace) {
      _isLoading = false;
      await FirebaseCrashlytics.instance.recordError(error, stackTrace);
      showCustomSnackBar('invalid_otp'.tr);
      update();
    }
  }

  void verifyOTPWithOption(String otp, String phone) async {
    _isLoading = true;
    update();

    loginUserWithOption(otp).then((value) {
      _isLoading = false;
      if (!value.isSuccess) {
        showCustomSnackBar(value.message);
        return;
      }
      _isWaitingForOTP = false;
      if (value.message == 'newuser') {
        Get.to(SignUpScreen(phone: phone));
      } else {
        Get.toNamed(RouteHelper.getInitialRoute("SplashScreen"));
      }
      update();
    });
  }

  Future<ResponseModel> sendOTPWithOption(
      String _phone, String countryDialCode) async {
    _isLoading = true;
    update();

    String _numberWithCountryCode = countryDialCode + _phone;
    bool _isValid = GetPlatform.isWeb ? true : false;
    if (!GetPlatform.isWeb) {
      if (_numberWithCountryCode.length == 13) {
        _isValid = true;
      } else {
        _isLoading = false;
        update();
      }
    }
    if (_phone.isEmpty) {
      showCustomSnackBar('enter_phone_number'.tr);
    } else if (!_isValid) {
      showCustomSnackBar('invalid_phone_number'.tr);
    } else {
      _signUpBody = SignUpBody(phone: _phone, countryCode: countryDialCode);
      Response response = await authRepo.sendOTP(_signUpBody);
      ResponseModel responseModel;
      if (response.statusCode == 200) {
        _isLoading = false;
        setVerificationId(verificationId);
        waitForOTP(true);
        responseModel = ResponseModel(true, response.statusText);
      } else {
        waitForOTP(false);
        responseModel = ResponseModel(false, response.statusText);
        ApiChecker.checkApi(response);
      }
      _isLoading = false;
      update();
      return responseModel;
    }
    return ResponseModel(true, "Logged in");
  }

  Future<ResponseModel> loginUserWithOption(String otp) async {
    ResponseModel responseModel;
    SignInBody signInBody = SignInBody(
        phone: _signUpBody.phone,
        countryCode: _signUpBody.countryCode,
        otp: otp);
    Response res = await authRepo.loginWithOption(signInBody);
    if (res.statusCode == 200) {
      authRepo.saveUserToken(res.body['token']);
      await updateToken();
      if (res.body['userName'] == null) {
        responseModel = ResponseModel(true, 'newuser');
      } else {
        responseModel = ResponseModel(true, 'olduser');
      }
    } else {
      responseModel = ResponseModel(false, res.statusText);
    }

    return responseModel;
  }

  Future<void> fetchOTPChannel(String countryCode, String phone) async {
    _isLoading = true;
    update();
    String url = Uri.parse(
            '${AppConstants.OTP_CHANNEL}?country_code=$countryCode&phone=$phone')
        .toString();

    try {
      final response = await authRepo.fetchOTPChannel(url);

      if (response.statusCode == 200) {
        final channel = response.body['channel'];

        _otpChannel = channel;

        if (channel == OtpChannel.firebase.name) {
          sendOTP(phone, countryCode);
        } else {
          sendOTPWithOption(phone, countryCode);
        }
      } else {
        // Handle the error
        print('Failed to load channel: ${response.statusCode}');
        ApiChecker.checkApi(response);
        _isLoading = false;
      }
    } catch (e) {
      // Handle the error
      print('Failed to load channel: $e');
      showCustomSnackBar('Failed to load otp channel');
      _isLoading = false;
    }
  }

  void setVerificationId(String verificationId) {
    _verificationId = verificationId;
    update();
  }

  void back() {
    _isLoading = false;
    update();
  }

  void stopLoading() {
    _isLoading = false;
  }
}

enum DeviceType { android, ios }
